package com.lemonboxstudio.drawboard.widget

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.RectF
import android.graphics.Xfermode
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewTreeObserver
import com.lemonboxstudio.drawboard.Config
import com.lemonboxstudio.drawboard.R
import com.lemonboxstudio.drawboard.data.DrawingInfo
import com.lemonboxstudio.drawboard.data.Mode
import com.lemonboxstudio.drawboard.data.PathDrawingInfo
import com.lemonboxstudio.drawboard.ktx.dp
import com.lemonboxstudio.drawboard.ktx.igNull

class DrawView : View, ViewTreeObserver.OnGlobalLayoutListener {

    private lateinit var paint: Paint
    private var path = Path()
    private lateinit var bufferCanvas: Canvas
    private lateinit var bufferBitmap: Bitmap
    private lateinit var eraserBitmap: Bitmap

    private var lastX: Float? = null
    private var lastY: Float? = null

    private val rectF = RectF()

    var drawPanSize = 2.dp
    var eraserSize = 4.dp

    var drawingList: ArrayList<DrawingInfo> = arrayListOf()
    var removeList: ArrayList<DrawingInfo> = arrayListOf()

    private var canEraser: Boolean = false

    private var hasTouch: Boolean = false

    private lateinit var xFerModeClear: Xfermode
    private lateinit var xFerModeDraw: Xfermode

    private var mode: Mode = Mode.DRAW

    private var callBack: DrawCallBack? = null

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    init {
        viewTreeObserver.addOnGlobalLayoutListener(this)
    }

    private fun init() {
        initPaint()
        initBuffer()
    }

    private fun initBuffer() {
        bufferBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        bufferBitmap.let { bufferCanvas = Canvas(it) }

        eraserBitmap = BitmapFactory.decodeResource(resources, R.drawable.erase2)
    }

    private fun initPaint() {
        xFerModeDraw = PorterDuffXfermode(PorterDuff.Mode.SRC)
        xFerModeClear = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
        paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
            this.color = Color.BLACK
            this.style = Paint.Style.STROKE
            this.strokeCap = Paint.Cap.ROUND
            this.strokeWidth = drawPanSize
            this.isAntiAlias = true
            this.xfermode = xFerModeDraw
        }
    }

    fun setOnDrawCallBack(callBack: DrawCallBack) {
        this.callBack = callBack
    }

    fun getMode(): Mode {
        return mode
    }

    fun setMode(mode: Mode) {
        if (this.mode != mode) {
            this.mode = mode
            if (mode == Mode.DRAW) {
                paint.xfermode = null
                paint.strokeWidth = drawPanSize
            } else {
                paint.xfermode = xFerModeClear
                paint.strokeWidth = eraserSize
            }
        }
    }

    inline val canUndo get() = drawingList.size > 0
    inline val canRedo get() = removeList.size > 0

    fun undo() {
        val size = drawingList.size
        if (size > 0) {
            val info = drawingList.removeAt(size - 1)
            if (size == 1) {
                canEraser = false
            }
            removeList.add(info)
            reDraw()
            callBack?.onUndoRedoStatusChanged()
        }
    }

    fun redo() {
        val size = removeList.size
        if (size > 0) {
            val info = removeList.removeAt(size - 1)
            drawingList.add(info)
            canEraser = true
            reDraw()
            callBack?.onUndoRedoStatusChanged()
        }
    }

    fun clear() {
        bufferBitmap.let {
            if (drawingList.isNotEmpty()) {
                drawingList.clear()
            }
            if (removeList.isNotEmpty()) {
                removeList.clear()
            }
            canEraser = false
            it.eraseColor(Color.TRANSPARENT)
            invalidate()
            callBack?.onUndoRedoStatusChanged()
        }
    }

    private fun reDraw() {
        bufferBitmap.eraseColor(Color.TRANSPARENT)
        drawingList.map { info ->
            bufferCanvas.let { canvas ->
                info.onDraw(canvas)
            }
        }
        invalidate()
    }

    private fun saveDrawPath() {
        if (drawingList.size == Config.MAX_CACHE_STEP) {
            drawingList.removeAt(0)
        }

        val cachePath = Path(path)
        val cachePaint = Paint(paint)
        val cacheInfo = PathDrawingInfo(cachePaint, cachePath)
        drawingList.add(cacheInfo)
        canEraser = true
        callBack?.onUndoRedoStatusChanged()
    }


    override fun onDraw(canvas: Canvas) {
        //绘制
        bufferBitmap.let {
            canvas.drawBitmap(it, 0f, 0f, null)
        }
        //
        if (mode == Mode.ERASER && hasTouch ) {
            canvas.drawBitmap(eraserBitmap,rectF.left,rectF.top,null)
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        val x = event.x
        val y = event.y
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                hasTouch = true
                lastX = x
                lastY = y
                path.moveTo(x.igNull(), y.igNull())
            }
            MotionEvent.ACTION_MOVE -> {
                rectF.set(
                    x - eraserBitmap.width / 2,
                    y - eraserBitmap.height / 2,
                    x + eraserBitmap.width / 2,
                    y - eraserBitmap.height / 2
                )
                if (mode == Mode.ERASER) {
                    // 橡皮擦
                    paint.xfermode = xFerModeClear
                    bufferCanvas.drawBitmap(eraserBitmap, rectF.left,rectF.top, paint)
                    paint.xfermode = null
                } else {
                    path.quadTo(
                        lastX.igNull(),
                        lastY.igNull(),
                        (x.igNull() + lastX.igNull()) / 2,
                        (y.igNull() + lastY.igNull()) / 2
                    )
                    bufferCanvas.drawPath(path,paint)
                }
                lastY = y
                lastX = x
                invalidate()
            }
            MotionEvent.ACTION_UP -> {
                hasTouch = false
                if (mode == Mode.DRAW || canEraser) {
                    saveDrawPath()
                }
                path.reset()
            }
            else -> {}
        }
        return true
    }
    override fun onGlobalLayout() {
        viewTreeObserver.removeOnGlobalLayoutListener(this)
        init()
    }
}